#ifndef ___fuse_main
#define ___fuse_main 1

#ifndef FUSE_USE_VERSION
#define FUSE_USE_VERSION 28
#endif

#include <stdio.h>
#include <stdlib.h>
#include "mtlib.c"
//archivo con funciones para envio de mensajes
#include <fuse.h>
//biblioteca para poder implementar funciones del fuse
#include <fcntl.h>
//opciones de archivo
#include <errno.h>
//numeros de errores que devuelven las funciones
#include "crear_consola.c"
#include <pthread.h>
#include "f32_main.c"
//para emplear la funcion del sistema fat
#include "hora.c"
#include "logging.c"
#include <sys/types.h>
//funcion wait()
#include <sys/wait.h>
//funcion wait()
#include <semaphore.h>
// manejo de senales
#include <signal.h>

//Prototipos de funciones auxiliares
void * ___fuse_crear(void);
void * ___fuse_flushear_datos_tiempo(void);
int * ___fuse_corte(void);
void ___fuse_destruir_semaforo(sem_t *);

void fuse_fat32_bloquear_senal(void);

//Defino variable s globales
unsigned int descriptor;
unsigned char centro_fuse = 0;
unsigned int fat32_listo = 0;
int argc_fuse;
int centro_fat;
char ** argv_fuse;
FILE * ptr;
int descArch;
int * ptrCorte = NULL;
sem_t * semCorte; //declaro una variable de tipo semaforo
#define corte ( * (___fuse_corte()) ) //defino variable compartida entre threads

//incluyo todos los archivos con las funciones del FUSE
#include "fuse_create.c"
#include "fuse_open.c"
#include "fuse_read.c"
#include "fuse_write.c"
#include "fuse_flush.c"
#include "fuse_release.c"
#include "fuse_truncate.c"
#include "fuse_unlink.c"
#include "fuse_mkdir.c"
#include "fuse_readdir.c"
#include "fuse_rmdir.c"
#include "fuse_getattr.c"
#include "fuse_rename.c"

//Estructura con punteros a funciones a implementar del fuse
struct fuse_operations fuse_oper = {
		.readdir = fuse_readdir,
		.rename = fuse_rename,
		.truncate = fuse_truncate,
		.read = fuse_read,
		.open = fuse_open,
		.getattr = fuse_getattr,
		.rmdir = fuse_rmdir,
		.flush = fuse_flush,
		.release = fuse_release,
		.create = fuse_create,
		.mkdir = fuse_mkdir,
		.write = fuse_write,
		.unlink = fuse_unlink,
};

int main (int argc, char ** argv) {
    
	int resultado;
	pthread_t idThread, idThread2;
	ptrCorte = NULL;
  
  fuse_fat32_bloquear_senal();
	
		ptr = fopen("logging.log","w"); //abro archivo de logueo en modo escritura
		descArch = fileno(ptr);

		___pfs_crear_consola(); //creo la consola como un proceso

		centro_fuse = mtf_obtener_descriptor(1024); //creo centro de mensajes para mandarme mensajes con el sistema fat

		argc_fuse = argc;
		argv_fuse = argv;

				while (centro_fuse == 0)
					centro_fuse = mtf_obtener_descriptor(1024); //creo centro de mensajes para mandarme mensajes con el sistema fat

				descriptor = mtf_conectar(centro_fuse, MTP_INTERNO, centro_fat); //me conecto con el centro del sistema fat,,
				//no utilizo ninguna flia de protocolos,, es memoria compartida al ser el mismo proceso

					if (descriptor == 0) {
					    mtf_liberar_descriptor(centro_fuse);
					    return -EAGAIN;

					} else {

					// ejecuto thread para crear el sistema fat
					fat32_crear_thread(centro_fat);

					//hasta que no me llegue el mensaje del sistema fat indicandome que ya esta listo,, no puedo mandarle ningun mensaje
					while (fat32_listo == 0) {

						if (mtf_recibir_mensajes(centro_fuse) == 0) continue;

						mti_mensaje datosMensaje = mtf_siguiente_mensaje(centro_fuse);

						if (datosMensaje.remitente != descriptor) {
							mtf_descartar_mensaje(centro_fuse, datosMensaje.codigo);
							continue;
						}

						if (datosMensaje.categoria != MTA_MENSAJE) {
							continue;
						}

						if (datosMensaje.tipo == MTQ_FAT32_OPERACIONAL) {
							fat32_listo = 1;
							log_registrar(descArch, "FUSE", LOG_INFO, "Esta disponible fat \n");
						}
					}
					
					//creo un	 thread para fuse_main para que al finalizar pueda liberar y desconectar el centro
					pthread_create(&idThread, NULL, (void *)&___fuse_crear, NULL);
				    //creo un thread para hacer un flush cada 5 segundos
                    pthread_create(&idThread2, NULL,(void *)&___fuse_flushear_datos_tiempo, NULL);
					     
                    pthread_join(idThread, NULL);    
                   	pthread_join(idThread2, NULL);
					
					fclose(ptr); // cierro archivo de logueo

						resultado = mtf_enviar_mensaje(centro_fuse, descriptor, MTQ_CERRAR_CONSOLA_FUSE);

							if (resultado == 0) {
							    mtf_desconectar(centro_fuse, descriptor);
							    mtf_liberar_descriptor(centro_fuse);
								return -EAGAIN;

							} else {
								wait(NULL); //espero a que se cierre el proceso de la consola
								mtf_desconectar(centro_fuse,descriptor); //desconecto la conexion del centro de mensajes
					            mtf_liberar_descriptor(centro_fuse); //libero centro de mensajes con sus datos y conexiones
								return EXIT_SUCCESS;
							}
				}
}

void * ___fuse_crear(void) { //invoco la funcion main del fuse donde le paso un puntero a una estructura con las funciones
//del fuse implementadas

		fuse_main(argc_fuse, argv_fuse, &fuse_oper, NULL);
		return NULL;

}

void * ___fuse_flushear_datos_tiempo(void) { //funcion para que me haga un flush en los datos de la cache
//cada 5 segundos

	long double horaActual = hora();
	int corte2;
    
        sem_wait(semCorte);
        corte2 = corte;
        sem_post(semCorte);
                    
		while(corte2) {
            
            if (hora() - horaActual > 5) {

			mtf_enviar_mensaje(centro_fuse, descriptor, MTQ_ESCRIBIR_TODO_CACHE);
			horaActual = hora();
			log_registrar(descArch, "FUSE", LOG_DEBUG, "Flush todos lo datos cada 5 segundos \n");
		    }        
					
            sem_wait(semCorte); //decremento el semaforo en uno
            corte2 = corte;
            sem_post(semCorte); //aumento el semaforo en uno
                    
		}
		
	___fuse_destruir_semaforo(semCorte);
	return NULL;
}         
	
//retorno variable global compartida entre threads
int * ___fuse_corte(void) {
    
    if (ptrCorte) {
        return ptrCorte;
        
    } else {
        ptrCorte = malloc(sizeof(int));
        semCorte = malloc(sizeof(sem_t));
        sem_init(semCorte,0 ,1);
        return ptrCorte;
    }
}

void ___fuse_destruir_semaforo(sem_t * semCorte) {
    
    sem_destroy(&semCorte);    
}

void fuse_fat32_bloquear_senal(void) {
    sigset_t s;
    sigemptyset(&s);
    sigaddset(&s, SIGUSR1);
    pthread_sigmask(SIG_BLOCK, &s, NULL);
}

#endif
